package com.ysw;

import com.google.common.collect.Lists;
import com.ysw.processor.ConfigProcessor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

/**
 * @ClassName ConfigContext 后期可以自己进行定义信息的修改
 * @Description 管理配置上下文信息
 * @Author ysw
 * @Date 2021/10/29 0:30
 */
@Data
public class ConfigContext {

    private final Logger logger = LoggerFactory.getLogger(ConfigContext.class);
    /**
     * spring 容器上下文使用
     */
    private AnnotationConfigServletWebServerApplicationContext applicationContext;
    /**
     * 维护所有processor
     */
    private final List<ConfigProcessor> processors;
    /**
     * 这里自己实现的时候写死，其实这里可以根据具体的服务环境来进行配置
     */
    public static final String ENV_CONFIG_NAME = "custom.config.properties";

    private ConfigContext() {
        processors = new ArrayList<>(10);
    }

    private static class ConfigContextHolder {
        private static final ConfigContext INSTANCE = new ConfigContext();
    }

    /**
     * 属性构造判断
     *
     * @param context
     * @return
     */
    public static ConfigContext getInstance(AnnotationConfigServletWebServerApplicationContext context) {
        if (null == ConfigContextHolder.INSTANCE.applicationContext) {
            synchronized (ConfigContext.class) {
                if (null == ConfigContextHolder.INSTANCE.applicationContext) {
                    ConfigContextHolder.INSTANCE.applicationContext = context;
                    return ConfigContextHolder.INSTANCE;
                }
            }
        }
        return ConfigContextHolder.INSTANCE;
    }

    /**
     * 获取已经存在的实例
     * @return
     */
    public static ConfigContext getExistInstance() {
        if (null == ConfigContextHolder.INSTANCE) {
            throw new RuntimeException("no instance");
        }
        return ConfigContextHolder.INSTANCE;
    }

    /**
     * 初始化所有processor
     */
    public void initAllProcessor() {
        this.processors.forEach(e -> e.initConfig(ConfigContextHolder.INSTANCE));
    }

    /**
     * 反射实例化所有processor
     */
    public void instanceAllProcessor() {
        // 从 spring.factories 中加载processors
        LinkedList<String> classNames = Lists.newLinkedList(SpringFactoriesLoader.loadFactoryNames(ConfigProcessor.class, ConfigProcessor.class.getClassLoader()));
        if (CollectionUtils.isEmpty(classNames)) {
            logger.warn("have no processors for init");
            return;
        }
        ClassLoader classLoader = ConfigContext.class.getClassLoader();
        classNames.forEach(className -> {
            try {
                Class<?> processorClass = ClassUtils.forName(className, classLoader);
                Constructor<?> processorConstructor = processorClass.getDeclaredConstructor();
                ConfigProcessor configProcessor = (ConfigProcessor) processorConstructor.newInstance();
                ConfigContextHolder.INSTANCE.processors.add(configProcessor);
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                logger.error("error init processor {}", e);
            }
        });

    }

    /**
     * 添加postProcessor to spring
     */
    public void addBeanPostProcessorToSpring() {
        ConfigContextHolder.INSTANCE.getProcessors().forEach(processor -> {
            if (processor instanceof BeanPostProcessor) {
                ConfigContextHolder.INSTANCE.applicationContext.getBeanFactory().addBeanPostProcessor((BeanPostProcessor) processor);
            }
        });
    }

    /**
     * 添加配置信息到spring environment 管理
     *
     * @param properties 外部配置信息
     */
    public void mergePropertiesToEnvironment(Properties properties) {
        ConfigContextHolder.INSTANCE.getApplicationContext()
                .getEnvironment()
                .getPropertySources()
                .addFirst(new PropertiesPropertySource(ConfigContext.ENV_CONFIG_NAME, properties));
    }

}